#include "networkNode.h"
#include "vector2.h"
#include "vector3.h"
#include "quaternion.h"

#include <cstdio>

namespace sde {
    
    NetworkNode::NetworkNode(bool _server) :
        server(_server),
        host(0),
        peer(0) {
    }
    
    void NetworkNode::connect(std::string hostName, int port) {
        if (!enet_initialize()) {
            if (server) {
                ENetAddress address;
                address.host = ENET_HOST_ANY;
                address.port = port;
                host = enet_host_create(&address,32,0,0);
                printf("hosting on port %d...\n",port);
            } else {
                host = enet_host_create(0,1,0,0);
                ENetAddress address;
                if (hostName == "") hostName = "localhost";
                enet_address_set_host(&address,hostName.c_str());
                address.port = port;
                printf("connecting to %s:%d...\n",hostName.c_str(),port);
                peer = enet_host_connect(host,&address,2);
            }
        }
    }
    
    void NetworkNode::disconnect() {
        if (server) {
        } else {
            if (peer) {
                printf("disconnecting...\n");
            
                enet_peer_disconnect(peer,0);
                
                while (enet_host_service(host,&event,1000)) {
                    switch (event.type) {
                    case ENET_EVENT_TYPE_RECEIVE:
                        enet_packet_destroy(event.packet);
                        break;
                    case ENET_EVENT_TYPE_DISCONNECT:
                        onDisconnected(event.peer);
                        return;
                    }
                }
                
                printf("forcing disconnect\n");
                enet_peer_reset(peer);
            }
        }
        enet_deinitialize();
    }
    
    void NetworkNode::pump(int time) {
        while (enet_host_service(host,&event,time)) {
            switch (event.type) {
            case ENET_EVENT_TYPE_CONNECT:
                onConnected(event.peer);
                break;
            case ENET_EVENT_TYPE_DISCONNECT:
                onDisconnected(event.peer);
                break;
            case ENET_EVENT_TYPE_RECEIVE:
                receive(event.peer,event.packet);
                break;
            }
        }
    }
    
    ENetPacket* NetworkNode::createPacket() {
        return enet_packet_create(0,0,ENET_PACKET_FLAG_RELIABLE);
    }
    
    void NetworkNode::send(ENetPeer* target, ENetPacket* packet) {
        if (host && target) {
            enet_peer_send(target,0,packet);
            enet_host_flush(host);
    //         enet_packet_destroy(packet);
        }
    }
    
    void NetworkNode::writeBool(ENetPacket* packet, bool b) {
        writeChar(packet,b);
    }
    
    void NetworkNode::writeChar(ENetPacket* packet, unsigned char c) {
        enet_packet_resize(packet,packet->dataLength+1);
        packet->data[packet->dataLength-1] = c;
    }
    
    void NetworkNode::writeInt(ENetPacket* packet, int i) {
        static const int size = sizeof(int);
        int pos = packet->dataLength;
        enet_packet_resize(packet,pos+size);
        memcpy(&packet->data[pos],&i,size);
    }
    
    void NetworkNode::writeFloat(ENetPacket* packet, float f) {
        static const int size = sizeof(float);
        int pos = packet->dataLength;
        enet_packet_resize(packet,pos+size);
        memcpy(&packet->data[pos],&f,size);
    }
    
    void NetworkNode::writeString(ENetPacket* packet, std::string s) {
        int size = s.size() + 1;
        writeInt(packet,size);
        int pos = packet->dataLength;
        enet_packet_resize(packet,pos+size);
        memcpy(&packet->data[pos],s.c_str(),size);
    }
    
    void NetworkNode::writeVar(ENetPacket* packet, Var v) {
        if (boost::any_cast<bool>(&v)) {
            writeChar(packet,boolData);
            writeBool(packet,boost::any_cast<bool>(v));
        } else if (boost::any_cast<int>(&v)) {
            writeChar(packet,intData);
            writeInt(packet,boost::any_cast<int>(v));
        } else if (boost::any_cast<float>(&v)) {
            writeChar(packet,floatData);
            writeFloat(packet,boost::any_cast<float>(v));
        } else if (boost::any_cast<std::string>(&v)) {
            writeChar(packet,stringData);
            writeString(packet,boost::any_cast<std::string>(v));
        } else if (boost::any_cast<Vector2>(&v)) {
            writeChar(packet,vector2Data);
            Vector2 vec = boost::any_cast<Vector2>(v);
            writeFloat(packet,vec.x);
            writeFloat(packet,vec.y);
        } else if (boost::any_cast<Vector3>(&v)) {
            writeChar(packet,vector3Data);
            Vector3 vec = boost::any_cast<Vector3>(v);
            writeFloat(packet,vec.x);
            writeFloat(packet,vec.y);
            writeFloat(packet,vec.z);
        } else if (boost::any_cast<Quaternion>(&v)) {
            writeChar(packet,quaternionData);
            Quaternion q = boost::any_cast<Quaternion>(v);
            writeFloat(packet,q.w);
            writeFloat(packet,q.x);
            writeFloat(packet,q.y);
            writeFloat(packet,q.z);
        } else {
            writeChar(packet,nullData);
        }
    }
    
    void NetworkNode::writeVarMap(ENetPacket* packet, VarMap vars) {
        writeInt(packet,vars.size());
        for (VarMap::iterator i = vars.begin(); i != vars.end(); ++i) {
            writeString(packet,i->first);
            writeVar(packet,i->second);
        }
    }
    
    bool NetworkNode::readBool(ENetPacket* packet, int& pos) {
        return packet->data[pos++];
    }
    
    unsigned char NetworkNode::readChar(ENetPacket* packet, int& pos) {
        return packet->data[pos++];
    }
    
    int NetworkNode::readInt(ENetPacket* packet, int& pos) {
        static const int size = sizeof(int);
        int i;
        memcpy(&i,&packet->data[pos],size);
        pos += size;
        return i;
    }
    
    float NetworkNode::readFloat(ENetPacket* packet, int& pos) {
        static const int size = sizeof(float);
        float f;
        memcpy(&f,&packet->data[pos],size);
        pos += size;
        return f;
    }
    
    std::string NetworkNode::readString(ENetPacket* packet, int& pos) {
        int size = readInt(packet,pos);
        char* str = new char[size];
        memcpy(str,&packet->data[pos],size);
        pos += size;
        std::string s(str);
        delete [] str;
        return s;
    }
    
    Var NetworkNode::readVar(ENetPacket* packet, int& pos) {
        unsigned char varType = readChar(packet,pos);
        float x,y,z,w;
        switch (varType) {
        case boolData:
            return readBool(packet,pos);
        case intData:
            return readInt(packet,pos);
        case floatData:
            return readFloat(packet,pos);
        case stringData:
            return readString(packet,pos);
        case vector2Data:
            x = readFloat(packet,pos);
            y = readFloat(packet,pos);
            return Vector2(x,y);
        case vector3Data:
            x = readFloat(packet,pos);
            y = readFloat(packet,pos);
            z = readFloat(packet,pos);
            return Vector3(x,y,z);
        case quaternionData:
            w = readFloat(packet,pos);
            x = readFloat(packet,pos);
            y = readFloat(packet,pos);
            z = readFloat(packet,pos);
            return Quaternion(w,x,y,z);
        default:
            return Var();
        }
    }
    
    VarMap NetworkNode::readVarMap(ENetPacket* packet, int& pos) {
        VarMap vars;
        int size = readInt(packet,pos);
        for (int i = 0; i < size; ++i) {
            std::string name = readString(packet,pos);
            Var val = readVar(packet,pos);
            vars[name] = val;
        }
        return vars;
    }

}
